package Server;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import data.Record;

public class StationServerImpl implements Runnable {

	public String servername;

	int serverport;
	public HashMap<String, List<Record>> hashmap = new HashMap<String, List<Record>>();

	public String getServername() {
		return servername;
	}

	static String[] args;

	// constructor
	protected StationServerImpl(String servername, int serverport) throws IOException {
		super();

		this.servername = servername;
		this.serverport = serverport;
		createServerFile();
	}

	// creates text logs for server
	private void createServerFile() throws IOException {
		File parentDir = new File("ServerLogs");
		parentDir.mkdir();
		String fileName = getServername() + ".txt";
		File file = new File(parentDir, fileName);
		file.createNewFile(); // Creates file in folder ServerLogs
	}

	// creates criminal record and writes log to its server

	public String createCRecord(String firstName, String lastName,
			String description, String status, String badgeID) {
		Record cr = new Record("CR");
		cr.setLastName(lastName);
		cr.setFirstName(firstName);
		cr.setDescription(description);
		cr.setStatus(status);
		cr.setOfficerID(badgeID);
		writeRecord(cr);

		String result = "{code:100, message:Added record., data:{recordID:"
				+ cr.getUniqueID() + ",firstName:" + cr.getFirstName()
				+ ",lastName:" + cr.getLastName() + ",status:" + cr.isStatus()
				+ ",crimeDescription:" + cr.getDescription() + "}}";

		return result;

	}

	// creates missing record and writes log to its server

	public String createMRecord(String firstName, String lastName,
			String description, String address, String lastDate,
			String lastLocation, String status, String badgeID) {
		// TODO Auto-generated method stub
		Record mr = new Record("MR");
		mr.setLastName(lastName);
		mr.setFirstName(firstName);
		mr.setDescription(description);
		mr.setAddress(address);

		mr.setLastSeenDate(lastDate);
		mr.setLastSeenPlace(lastLocation);
		mr.setOfficerID(badgeID);
		mr.setStatus(status);
		writeRecord(mr);

		String result = "{code:200, message:Added record., data:{recordID:"
				+ mr.getUniqueID() + ",firstName:" + mr.getFirstName()
				+ ",lastName:" + mr.getLastName() + ",status:" + mr.isStatus()
				+ ",crimeDescription:" + mr.getDescription() + ",address:"
				+ mr.getAddress() + ",lastDate:" + mr.getLastSeenDate()
				+ ",lastLocation:" + mr.getLastSeenPlace() + "}}";

		return result;

	}

	// write recod to server's text file
	private synchronized void writeRecord(Record r) {
		try {

			PrintWriter out = new PrintWriter(new BufferedWriter(
					new FileWriter("ServerLogs/"
							+ getStationCode(r.getOfficerID()) + ".txt", true)));
			out.println("RecordID:-" + r.getUniqueID() + ", Officer badgeID:-"
					+ r.getOfficerID() + ", FirstName:-" + r.getFirstName()
					+ ", LastName:-" + r.getLastName() + ", Address:-"
					+ r.getAddress() + ", Last seen date:-"
					+ r.getLastSeenDate() + ", Last seen place:-"
					+ r.getLastSeenPlace() + ", Status:-" + r.isStatus());
			out.close();
		} catch (IOException e) {
			// oh noes!
		}
		String letter = r.getLastName().substring(0,
				Math.min(r.getLastName().length(), 1));
		List<Record> temprecordlist = new ArrayList<Record>();

		synchronized (temprecordlist) {
			if (hashmap.containsKey(letter)) {
				temprecordlist = hashmap.get(letter);
				temprecordlist.add(r);
				// hashmap.put(letter, temprecordlist);
			} else {
				temprecordlist.add(r);
				hashmap.put(letter, temprecordlist);
			}
		}

		writeOfficerLog(r);
	}

	private String getStationCode(String badgeID) {
		String Stationcode;
		if (badgeID.charAt(2) == 'V') {
			Stationcode = badgeID.substring(0, Math.min(badgeID.length(), 4));
		} else {
			Stationcode = badgeID.substring(0, Math.min(badgeID.length(), 3));
		}
		return Stationcode;

	}

	// write details in office's text file
	private synchronized void writeOfficerLog(Record r) {

		try {

			PrintWriter out = new PrintWriter(new BufferedWriter(
					new FileWriter("OfficerLogs/" + r.getOfficerID() + ".txt",
							true)));
			out.println("RecordID:-" + r.getUniqueID() + ", Officer badgeID:-"
					+ r.getOfficerID() + ", FirstName:-" + r.getFirstName()
					+ ", LastName:-" + r.getLastName() + ", Address:-"
					+ r.getAddress() + ", Last seen date:-"
					+ r.getLastSeenDate() + ", Last seen place:-"
					+ r.getLastSeenPlace() + ", Status:-" + r.isStatus());
			out.close();
		} catch (IOException e) {
			// oh noes!
		}
	}

	// retuns record count of each server

	public String getRecordCount(String badgeID) {
		String result = "{code:300, message:Record count., data:{"
				+ requestRecordCount() + "}}";
		return result;
	}

	private String requestRecordCount() {

		StringBuilder sb = new StringBuilder();
		String s1, s2, s3;
		switch (servername) {
		case "SPVM":
			s3 = "SPVM:" + Integer.toString(getOwnRecordCount());
			sb.append(s3);
			s1 = ",SPL:" + CreateClientUDP("SPL", 6880);
			sb.append(s1);
			s2 = ",SPB:" + CreateClientUDP("SPB", 6881);
			sb.append(s2);
			break;
		case "SPL":
			s1 = "SPVM:" + CreateClientUDP("SPVM", 6879);
			sb.append(s1);
			s3 = ",SPL:" + Integer.toString(getOwnRecordCount());
			sb.append(s3);
			s2 = ",SPB:" + CreateClientUDP("SPB", 6881);
			sb.append(s2);
			break;
		case " SPB":
			s2 = "SPVM:" + CreateClientUDP("SPVM", 6879);
			sb.append(s2);
			s1 = ",SPL:" + CreateClientUDP("SPL", 6880);
			sb.append(s1);
			s3 = ",SPVB:" + Integer.toString(getOwnRecordCount());
			sb.append(s3);
			break;

		}
		return sb.toString();
	}

	// Create UPD client for requsting record count of other server
	private String CreateClientUDP(String Servername, int port) {
		DatagramSocket aSocket = null;
		try {
			aSocket = new DatagramSocket();
			String s = "7";
			byte[] m = new byte[1000];
			m = s.getBytes();
			InetAddress aHost = InetAddress.getByName("localhost");

			DatagramPacket request = new DatagramPacket(m, m.length, aHost,
					port);
			aSocket.send(request);
			byte[] buffer = new byte[1000];
			DatagramPacket reply = new DatagramPacket(buffer, buffer.length);
			aSocket.receive(reply);
			String result = new String(reply.getData()).trim();

			return result;

		} catch (SocketException e) {
			System.out.println("Socket:" + e.getMessage());
		} catch (IOException e) {
			System.out.println("IO:" + e.getMessage());
		} finally {
			if (aSocket != null)
				aSocket.close();
		}
		return Servername;

	}

	// returns servers own record count
	public int getOwnRecordCount() {
		int size = 0;
		for (Map.Entry<String, List<Record>> entry : hashmap.entrySet()) {
			List<Record> templist = entry.getValue();
			size = size + templist.size();
		}

		return size;
	}

	// edit record,set new status of record

	public synchronized String editCRecord(String lastName, String recordID,
			String newStatus, String badgeID) {
		String letter = Character.toString(lastName.charAt(0));

		List<Record> templist = hashmap.get(letter);
		if (templist == null)
			return "{code:-100, message:Record not found.}";
		else {
			for (int i = 0; i < templist.size(); i++) {
				if (templist.get(i).getUniqueID().equals(recordID)) {

					synchronized (templist) {
						templist.get(i).setStatus(newStatus);
						hashmap.put(letter, templist);
					}

					String result = "{code:400, message:Edited record., data:{recordID:"
							+ recordID
							+ ",firstName:"
							+ templist.get(i).getFirstName()
							+ ",lastName:"
							+ templist.get(i).getLastName()
							+ ",status:"
							+ newStatus
							+ ",crimeDescription:"
							+ templist.get(i).getDescription() + "}}";
					return result;
					// return true;
				}
			}
		}
		return "{code:-100, message:Record not found.}";
	}

	// run UDP server for each station
	@Override
	public void run() {

		DatagramSocket asocket = null;
		try {
			asocket = new DatagramSocket(serverport);
			byte[] buffer = new byte[10000];

			while (true) {
				DatagramPacket request = new DatagramPacket(buffer,
						buffer.length);
				asocket.receive(request);

				String requestMessage = new String(request.getData()).trim();
				//System.out.println(requestMessage);
				List<String> arrlist = Arrays.asList(requestMessage.split(","));

				byte[] m = null;
				String replyMessage = null;
				if (arrlist.get(0).equals("1")) {

					replyMessage = this.createCRecord(arrlist.get(1),
							arrlist.get(2), arrlist.get(3), arrlist.get(4),
							arrlist.get(5));
					//System.out.println(replyMessage);

				} else if (arrlist.get(0).equals("2")) {
					replyMessage = this.createMRecord(arrlist.get(1),
							arrlist.get(2), arrlist.get(3), arrlist.get(4),
							arrlist.get(5), arrlist.get(6), arrlist.get(7),
							arrlist.get(8));
					//System.out.println(replyMessage);
				} else if (arrlist.get(0).equals("3")) {
					replyMessage = this.getRecordCount(arrlist.get(1));
					//System.out.println(replyMessage);

				} else if (arrlist.get(0).equals("4")) {
					replyMessage = this.editCRecord(arrlist.get(1),
							arrlist.get(2), arrlist.get(3), arrlist.get(4));
					//System.out.println(replyMessage);
				} else if (arrlist.get(0).equals("6")) {

					replyMessage = this.transferRecord(arrlist.get(1),
							arrlist.get(2), arrlist.get(3));
					//System.out.println(replyMessage);

				} else if (arrlist.get(0).equals("7")) {
					replyMessage = Integer.toString(this.getOwnRecordCount());
				} 
				else if (arrlist.get(0).equals("8")) {

					String theRecord = new String(request.getData());

					Record r = new Record(theRecord, true);

					String letter = r.getLastName().substring(0,
							Math.min(r.getLastName().length(), 1));
					List<Record> temprecordlist = new ArrayList<Record>();

					if (this.hashmap.containsKey(letter)) {
						temprecordlist = this.hashmap.get(letter);
						temprecordlist.add(r);
					} else {
						temprecordlist.add(r);
						this.hashmap.put(letter, temprecordlist);
					}
					System.out.println(this.getOwnRecordCount());
					replyMessage = "Success";

				}

				
					m = replyMessage.getBytes();
					DatagramPacket reply = new DatagramPacket(m,
							m.length, request.getAddress(),
							request.getPort());
					asocket.send(reply);
				
			}
		} catch (SocketException e) {
			System.out.println("Socket:" + e.getMessage());
		} catch (IOException e) {
			System.out.println("IO:" + e.getMessage());
		} finally {
			if (asocket != null)
				asocket.close();
		}

	}

	// transfer record to other server
	public synchronized String transferRecord(String badgeID, String recordID,
			String remoteStationServerName) {

		for (Map.Entry<String, List<Record>> entry : hashmap.entrySet()) {
			List<Record> templist = entry.getValue();
			for (int i = 0; i < templist.size(); i++) {

				// System.out.println(templist.get(i).getUniqueID());
				if (templist.get(i).getUniqueID().equals(recordID)) {
					// System.out.println(templist.get(i).getUniqueID()+"="+recordID);
					// create record in string
					String theRecord = "8,"+ templist.get(i).getUniqueID()
							+ "," + templist.get(i).getOfficerID() + ","
							+ templist.get(i).getFirstName() + ","
							+ templist.get(i).getLastName() + ","
							+ templist.get(i).getAddress() + ","
							+ templist.get(i).getLastSeenDate() + ","
							+ templist.get(i).getLastSeenPlace() + ","
							+ templist.get(i).isStatus();

					// transfer record to remote server
					boolean isTransfered = false;
					try {
						isTransfered = transferByUDP(theRecord,
								remoteStationServerName);
						// System.out.println(isTransfered);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					String result;
					// delete record from caller's hash table if transfer is
					// Successful
					//System.out.println(isTransfered);
					if (isTransfered) {

						result = "{code:500, Transferred record., data:{recordID:"
								+ recordID
								+ ",firstName:"
								+ templist.get(i).getFirstName()
								+ ",lastName:"
								+ templist.get(i).getLastName()
								+ ",status:"
								+ templist.get(i).isStatus()
								+ ",crimeDescription:"
								+ templist.get(i).getDescription() + "}}";
						// System.out.println(result);
						templist.remove(i);
						hashmap.put(entry.getKey(), templist);

						return result; // success
					}
				}
			}
		}
		return "{code:-100, message:Record not found.}"; // fail
	}

	// transfer record to remote UDP service
	private boolean transferByUDP(String theRecord,
			String remoteStationServerName) throws Exception {

		DatagramSocket aSocket = new DatagramSocket();
		int port = 0;
		String s = theRecord;
		byte[] m = s.getBytes();
		InetAddress aHost = InetAddress.getByName("localhost");

		if (remoteStationServerName.charAt(2)=='B') {
			port = 6881;
		} else if (remoteStationServerName.charAt(2)=='L') {
			port = 6880;
		} else if (remoteStationServerName.charAt(2)=='V') {
			port = 6879;
		}
		DatagramPacket request = new DatagramPacket(m, m.length, aHost, port);
		aSocket.send(request);

		return true;
	}

}
